#ifdef WIN32
#include <windows.h>
#undef RGB
#endif

#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <GLUT/glut.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#endif

#include <SMT.h>
#include <math.h>

#define PI	3.14159265f

#include <stdio.h>

#define CIRCLE_DIVISIONS	128

#define RGB(r,g,b)	(((r)<<16)|((g)<<8)|(b))
#define R(c)		((c)>>16)
#define G(c)		(((c)>>8)&0xFF)
#define B(c)		((c)&0xFF)

static int colors[4] =
{
	RGB(255,0,0),
	RGB(0,255,0),
	RGB(0,0,255),
	RGB(127,0,127)
};

static int window_width = 800;
static int window_height = 600;
static SMT_SENSOR s = 0;

static int fullscreen = 0;

void InitGL(void)
{
	glShadeModel(GL_SMOOTH);
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
	glClearDepth(1.0f);
}

void Update(void)
{	
	// update data from sensor
	if (!SMT_Update(s))
	{
		// if update fails, close cleanly and try to reopen
		//if (s) SMT_Close(s);
		//s = SMT_Open(0, window_width, window_height, 0, 0);
	}
	// request window refresh
	glutPostRedisplay();
}

void Resize(int width, int height)
{	
	glViewport(0, 0, width, height);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(0, width, height, 0);
		
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	
	// notify the sensor with the updated dimensions
	// this will automatically remap existing cursors in the virtual window space
	SMT_SetSensorWindowDimension(s, width, height);

	window_width = width;
	window_height = height;	
}

void drawCircle(float center_x, float center_y, float radius, int color)
{
	glColor4ub(R(color), G(color), B(color), 255); 
	glLineWidth(1.0f);
	glBegin(GL_LINE_LOOP);
	for (int div=0; div<CIRCLE_DIVISIONS; div++)
	{
		float angle = PI*2*div/(float)CIRCLE_DIVISIONS;
		float x = cosf(angle)*radius + center_x;
		float y = sinf(angle)*radius + center_y;
		glVertex2d(x, y);
	}
	glEnd();
}

void Redraw(void)
{	
	static int i = 0;

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();
	
	if (s)
	{
		// iterate on cursor list, and draw each cursor that's currently in SMT_CURSOR_DOWN state
		SMT_CURSOR c = SMT_GetSensorCursors(s);
		while (c)
		{
			// skip SMT_CURSOR_UP cursors waiting for multiclick
			if (SMT_GetCursorState(c)==SMT_CURSOR_DOWN)
			{
				// color is stored as cursor's user data
				int color = (int) SMT_GetCursorUserData(c);
				if (!color)
				{
					// if no color was stored, select next color and store it
					color = colors[i&3];
					SMT_SetCursorUserData(c, (void *) color);
					i++;
				}
				drawCircle(SMT_GetCursorX(c), SMT_GetCursorY(c), 10, color);
			}
			c = SMT_GetCursorNext(c);
		}
	}
	
	glutSwapBuffers();
}

void Keyboard(int key, int x, int y)
{
	switch (key)
	{
	case GLUT_KEY_F1:
		fullscreen = !fullscreen;
		if (fullscreen) glutFullScreen();
		else
		{
			glutPositionWindow(100,100);
			glutReshapeWindow(window_width, window_height);
		}
		break;
		
	default:
		break;
	}
}

int main(int argc, char** argv)
{
	// open connection to default SMK device, with dimensions corresponding to the current OpenGL window
	// callback is set to NULL, since we're reading the cursors list directly in the Redraw hook
	s = SMT_Open(0, window_width, window_height, 0, 0);
	if (!s)
	{
		printf("couldn't connect to default SMK device\n");
		return 0;
	}
	
	// init glut window
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(window_width, window_height);
	glutInitWindowPosition(100, 100);
	glutCreateWindow(argv[0]);

	InitGL();

	// Redraw hook will read the sensors' cursor list, and draw a circle for each
	glutDisplayFunc(Redraw);
	// Resize hook will adapt the sensor's virtual window to the new range
	glutReshapeFunc(Resize);
	// Update hook will handle the SMK data update, and reconnection in case the device is plugged out and back in
	glutIdleFunc(Update);
	// Keyboard hook switches to fullscreen mode on demand
	glutSpecialFunc(Keyboard);
		
	// let's go
	glutMainLoop();
	
	// close connection
	SMT_Close(s);
	
	return 0;
}
